/*
 * 描          述:  <描述>
 * 修  改   人:  Administrator
 * 修改时间:  2018年5月15日
 * <修改描述:>
 */
package com.tx.component.task.interceptor.impl;

import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;

import com.tx.component.task.exeception.TaskExecuteException;
import com.tx.component.task.interceptor.TaskExecuteSession;
import com.tx.component.task.interceptor.TaskExecuteSessionUtils;
import com.tx.component.task.model.TaskDef;
import com.tx.component.task.model.TaskStatus;
import com.tx.core.exceptions.util.AssertUtils;
import com.tx.core.util.MessageUtils;

/**
 * 任务执行时环境实现<br/>
 * <功能详细描述>
 * 
 * @author  Administrator
 * @version  [版本号, 2018年5月15日]
 * @see  [相关类/方法]
 * @since  [产品/模块版本]
 */
public class TaskExecuteSessionImpl implements TaskExecuteSession {
    
    private static final String NEXT_FIRE_DATE_KEY = "nextFireDate";
    
    private static final String NEXT_FIRE_DATE_FORMAT_PATTERN = "yyyy-MM-dd HH:mm:ss";
    
    /** 任务定义 */
    private final TaskDef taskDef;
    
    /** 任务状态 */
    private final TaskStatus taskStatus;
    
    /**
     * 任务执行时参数，从taskDef读取，再从taskStatus中读取其中的attributes,
     * 使用完毕后写入taskStatus
     * 注意nextFireDate也会写入该参数中作为冗余存储
     */
    private final Map<String, String> parameterMap;
    
    /**
     * 会话中传递的参数实例
     * 不作存储，仅在任务执行期间能够跨方法隐形传递参数
     */
    private final Map<String, Object> executionAttributeMap;
    
    /** 是否是被跳过的 */
    private boolean skipped = false;
    
    /** <默认构造函数> */
    public TaskExecuteSessionImpl(TaskDef taskDef, TaskStatus taskStatus) {
        super();
        AssertUtils.notNull(taskDef, "taskDef is null.");
        AssertUtils.notNull(taskStatus, "taskStatus is null.");
        AssertUtils.notEmpty(taskDef.getId(), "taskDef.id is empty.");
        
        this.taskDef = taskDef;
        this.taskStatus = taskStatus;
        this.parameterMap = new HashMap<>();
        this.executionAttributeMap = new HashMap<>();
        
        this.parameterMap.putAll(TaskExecuteSessionUtils
                .fromParameters(this.taskDef.getParameters()));
        this.parameterMap.putAll(TaskExecuteSessionUtils
                .fromParameters(this.taskStatus.getParameters()));
    }
    
    /**
     * @return
     */
    @Override
    public String getTaskId() {
        return this.taskDef.getId();
    }
    
    /**
     * @return
     */
    @Override
    public TaskDef getTask() {
        return this.taskDef;
    }
    
    /**
     * @return
     */
    @Override
    public TaskStatus getTaskStatus() {
        return this.taskStatus;
    }
    
    /**
     * 获取当前会话中是否被跳过的标志<br/>
     * @return boolean (true,false)
     */
    @Override
    public boolean isSkipped() {
        return skipped;
    }
    
    /**
     * 设置当前方法为跳过
     */
    @Override
    public void setSkipped() {
        this.skipped = true;
    }
    
    /**
     * @return
     */
    @Override
    public Map<String, String> getParameters() {
        return this.parameterMap;
    }
    
    /**
     * @param key
     * @param value
     */
    @Override
    public void setParameter(String key, String value) {
        AssertUtils.notEmpty(key, "key is empty.");
        
        this.parameterMap.put(key, value);
    }
    
    /**
     * @param key
     * @return
     */
    @Override
    public String getParameter(String key) {
        return this.parameterMap.get(key);
    }
    
    /**
     * @return
     */
    @Override
    public Date getNextFireDate() {
        Date nextFireDate = this.taskStatus.getNextFireDate();
        if (nextFireDate != null) {
            return nextFireDate;
        }
        String nextFireDateStr = this.parameterMap.get(NEXT_FIRE_DATE_KEY);
        if (StringUtils.isEmpty(nextFireDateStr)) {
            return null;
        }
        try {
            nextFireDate = DateUtils.parseDate(nextFireDateStr,
                    NEXT_FIRE_DATE_FORMAT_PATTERN,
                    "yyyy-MM-dd");
        } catch (ParseException e) {
            //donothing
            return null;
        }
        return nextFireDate;
    }
    
    /**
     * @param nextFireDate
     */
    @Override
    public void setNextFireDate(Date nextFireDate) {
        AssertUtils.notNull(nextFireDate, "nextFireDate is null.");
        this.taskStatus.setNextFireDate(nextFireDate);
        
        //将下次执行时间设置到参数中
        this.parameterMap.put(NEXT_FIRE_DATE_KEY,
                DateFormatUtils.format(nextFireDate,
                        NEXT_FIRE_DATE_FORMAT_PATTERN));
    }
    
    /**
     * @param key
     * @param value
     * @return
     */
    @Override
    public void setExecutionAttribute(String key, Object value) {
        AssertUtils.notEmpty(key, "key is empty.");
        this.executionAttributeMap.put(key, value);
    }
    
    /**
     * @param key
     * @return
     */
    @Override
    public Object getExecutionAttribute(String key) {
        AssertUtils.notEmpty(key, "key is empty.");
        
        Object res = this.executionAttributeMap.get(key);
        return res;
    }
    
    /**
     * @param <T>
     * @param key
     * @param type
     * @return
     */
    @SuppressWarnings("unchecked")
    @Override
    public <T> T getExecutionAttribute(String key, Class<T> type) {
        AssertUtils.notEmpty(key, "key is empty.");
        AssertUtils.notNull(type, "type is empty.");
        
        Object res = this.executionAttributeMap.get(key);
        if (type.isInstance(res)) {
            return (T) res;
        } else {
            throw new TaskExecuteException(MessageUtils.format(
                    "getExecutionAttribute error. 期望参数: {}, 类型为: {} ,实际类型为:{}",
                    new Object[] { key, type, res.getClass() }));
        }
    }
}
